package org.liang.transfer.ftp;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * FTP上传器.
 * 
 * <pre>
 * 整合FTP上传实现多有代码。
 * 此类仅向{@link FTP}开放访问
 * </pre>
 * 
 * @author L.Yang
 * @version v1.0 2016年4月11日 下午2:04:23
 */
public class FTPUploader {

    private static final Logger LOGGER = LoggerFactory.getLogger(FTPUploader.class);

    private FTPClient ftp;

    private InputStream input = null;

    public FTPUploader(FTPClient ftp) {
        super();
        this.ftp = ftp;
    }

    /**
     * 上传文件到服务器.
     * 
     * @param remote
     *            远程FTP服务器文件路径
     * @param local
     *            本地存储文件的完整路径（包含文件名）
     */
    public void upload(String remote, String local) {
        try {
            intoWorkDirectory(remote);
            uploadFile(local);
        } catch (IOException e) {
            LOGGER.error("FTP 上传失败 :{}", ExceptionUtils.getStackTrace(e));
        } finally {
            IOUtils.closeQuietly(input);
        }
    }

    private void intoWorkDirectory(String remote) throws IOException {
        String path = FilenameUtils.getFullPath(remote);
        String[] dirs = path.split("/");
        for (String dir : dirs) {
            ftp.mkd(dir);
            ftp.changeWorkingDirectory(dir);
        }
        LOGGER.trace("进入工作目录 : {}", path);
    }

    /**
     * 上传文件入口.
     * 
     * <pre>
     * 如果是目录则进行递归上传，将目录下所有文件夹、文件上传到服务器。
     * </pre>
     * 
     * @param local
     *            本地文件路径（文件/文件夹）
     * @throws IOException
     *             - If an I/O error occurs while either sending a command to the server or
     *             receiving a reply from the server.
     * @see {@link FTPClient#storeFile(String, InputStream)}
     */
    private void uploadFile(String local) throws IOException {
        File localfile = new File(local);
        if (localfile.isDirectory()) {
            recursiveupload(localfile);
        } else {
            storeFile(localfile);
        }
    }

    private void recursiveupload(File directory) throws IOException {
        for (File file : directory.listFiles()) {
            if (file.isDirectory()) {
                String path = file.getName();
                ftp.mkd(path);
                ftp.changeWorkingDirectory(path);
                LOGGER.trace("进入子文件夹： {} ", path);
                recursiveupload(file);
            } else {
                storeFile(file);
            }
        }
        LOGGER.trace("返回上级文件夹： {} ", directory.getName());
        ftp.changeToParentDirectory();
    }

    /**
     * 将文件上传到FTP服务器.
     * 
     * @param localfile
     *            本地文件对象
     * @throws IOException
     *             - If an I/O error occurs while either sending a command to the server or
     *             receiving a reply from the server.
     * @see {@link FTPClient#storeFile(String, InputStream)}
     */
    private void storeFile(File localfile) throws
                    IOException {
        input = new BufferedInputStream(new FileInputStream(localfile));
        ftp.storeFile(localfile.getName(), input);
        IOUtils.closeQuietly(input);
        LOGGER.trace("文件上传完成： {}", localfile.getName());
    }
}
